#include "include.h"

// The page needs to be pinned for this to succeed.
int sos_copyin_nofault(sos_pcb *pcb, seL4_Word proc_vaddr, seL4_Word src,
                       size_t nbyte, size_t *nsent) {
    int err;
    sos_region *region;
    page_table_entry *pte;
    seL4_Word dst;
    size_t page_remaining;

    // Check regions
    err = sos_resolve_region(&(pcb->regions), proc_vaddr, &region);
    if (err != SOS_REGION_SUCCESS) {
        // Access outside of any region - segfault
        return SOS_PAGE_TABLE_SEGFAULT;
    }

    // Check we can write to the region
    if (!region->attrs.writeable) {
        // Access violation
        return SOS_PAGE_TABLE_SEGFAULT;
    }

    // Walk the page table, nofault
    err = page_walk_nofault(pcb, proc_vaddr, &pte);
    if (err != SOS_PAGE_TABLE_SUCCESS) {
        // Regular page fault - invalid use of this function.
        return err;
    }

    // Frame must be mapped to proceed
    if (pte->attrs.swapped || pte->framenumber == 0) {
        return SOS_PAGE_TABLE_FAULT;
    }

    // Convert framenumber to sos_vaddr
    dst = frame_translate_number(pte->framenumber);
    // Be careful - something might have page-aligned the address!
    dst = PAGE_ALIGN(dst) + (proc_vaddr & PAGE_MASK);
    // Write up to the remainder of the page, at most, and not more than nbyte
    page_remaining = PAGE_SIZE - (proc_vaddr & PAGE_MASK);
    (*nsent) = MIN(nbyte, page_remaining);

    memcpy((void *) dst, (void *) src, *nsent);
    return SOS_PAGE_TABLE_SUCCESS;
}

// The page needs to be pinned for this to succeed.
int sos_copyout_nofault(sos_pcb *pcb, seL4_Word proc_vaddr, seL4_Word dst, 
                        size_t nbyte, size_t *nsent) {
    int err;
    sos_region *region;
    page_table_entry *pte;
    seL4_Word src;
    size_t page_remaining;

    // Check regions
    err = sos_resolve_region(&(pcb->regions), proc_vaddr, &region);
    if (err != SOS_REGION_SUCCESS) {
        // Access outside of any region - segfault
        return SOS_PAGE_TABLE_SEGFAULT;
    }

    // Check we can write to the region
    if (!region->attrs.readable) {
        // Access violation
        return SOS_PAGE_TABLE_SEGFAULT;
    }

    // Walk the page table, nofault
    err = page_walk_nofault(pcb, proc_vaddr, &pte);
    if (err != SOS_PAGE_TABLE_SUCCESS) {
        // Regular page fault - invalid use of this function.
        return err;
    }

    // Frame must be mapped to proceed
    if (pte->attrs.swapped || pte->framenumber == 0) {
        return SOS_PAGE_TABLE_FAULT;
    }

    // Convert framenumber to sos_vaddr
    src = frame_translate_number(pte->framenumber);
    // Be careful - something might have page-aligned the address!
    src = PAGE_ALIGN(src) + (proc_vaddr & PAGE_MASK);
    // Read up to the remainder of the page, at most, and not more than nbyte
    page_remaining = PAGE_SIZE - (proc_vaddr & PAGE_MASK);
    (*nsent) = MIN(nbyte, page_remaining);

    memcpy((void *) dst, (void *) src, *nsent);
    return SOS_PAGE_TABLE_SUCCESS;
}
